package com.systemsplanet.maven.plugin;

import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Properties;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

import com.google.api.translate.Language;
import com.google.api.translate.Translate;

/**
 * generate Language files
 *
 * @goal localize
 *
 * @phase generate-sources
 */
public class LocalizeMojo extends AbstractMojo {

    /**
     * Debug mode
     *
     * @parameter expression="false"
     *
     */
    private Boolean debug;

    /**
     * English translation source path
     *
     * @parameter expression="${basedir}/src/main/resources/"
     *
     */
    private File sourceTranslationPath;

    /**
     * English translation source file pattern
     *
     * @parameter expression="Language_{0}.properties"
     *
     */
    private String languageFilePattern;

    /**
     * English translation source language
     *
     * @parameter expression="en"
     *
     */
    private String sourceLanguage;
    /**
     * Destination directory for output files
     *
     * @parameter expression="${project.build.directory}/classes/"
     */
    private File destinationPath;

    /*
     * ARABIC = "ar"; CHINESE = "zh"; CHINESE_SIMPLIFIED = "zh-CN";
     * CHINESE_TRADITIONAL = "zh-TW"; DUTCH = "nl"; FRENCH = "fr"; GERMAN =
     * "de"; GREEK = "el"; ITALIAN = "it"; JAPANESE = "ja"; KOREAN = "ko";
     * PORTUGESE = "pt"; RUSSIAN = "ru"; SPANISH = "es";
     */

    /**
     * list of languages
     *
     * @parameter expression="es,fr,nl,de,it"
     */
    private String targetLanguages;

    @SuppressWarnings("unchecked")
    public void execute() throws MojoExecutionException, MojoFailureException {
        Object[] arguments = { sourceLanguage };
        String sourceTranslationFileName = MessageFormat.format(
                sourceTranslationPath + File.separator + languageFilePattern,
                arguments);
        File sourceTranslationFile = new File(sourceTranslationFileName);

        if (debug) {
            System.out.println("sourceTranslationPath: ["
                    + sourceTranslationPath + "]");
            System.out.println("languageFilePattern: [" + languageFilePattern
                    + "]");
            System.out.println("sourceLanguage: [" + sourceLanguage + "]");
            System.out.println("sourceTranslationFileName: ["
                    + sourceTranslationFileName + "]");
            System.out.println("sourceTranslationFile ["
                    + sourceTranslationFile.getAbsolutePath() + "]");
            System.out.println("destinationPath ["
                    + destinationPath.getAbsolutePath() + "]");
        }
        if (!destinationPath.exists())
            destinationPath.mkdirs();
        FileInputStream fis = null;
        BufferedWriter bw = null;
        try {
            String[] languages = targetLanguages.split(",");
            Properties p = new Properties();
            fis = new FileInputStream(sourceTranslationFile.getAbsolutePath());
            p.load(fis);
            fis.close();
            fis = null;
            for (String destLanguage : languages) {
                Object[] args = { destLanguage };
                String destTranslationFileName = MessageFormat.format(
                        destinationPath + File.separator + languageFilePattern,
                        args);
                System.out.print(destLanguage + " ");
                if (debug) {
                    System.out.println("destTranslationFileName:"
                            + destTranslationFileName);
                }
                bw = new BufferedWriter(new FileWriter(destTranslationFileName));
                Enumeration msgKeys = p.keys();
                try {
                    while (msgKeys.hasMoreElements()) {
                        String msgKey = (String) msgKeys.nextElement();
                        String msgVal = p.getProperty(msgKey);
                        if (debug) {
                            System.out.println(msgKey + "=[" + sourceLanguage
                                    + "]" + msgVal);
                        } else {
                            System.out.print(".");
                        }
                        bw.write(msgKey + "=");
                        if (debug) {
                            System.out
                                    .print(msgKey + "=[" + destLanguage + "]");
                        }
                        EventProcessor e = new EventProcessor(debug,
                                sourceLanguage, destLanguage, bw);
                        parse(new ByteArrayInputStream(msgVal.getBytes()), e);
                        bw.write("\r\n");
                    }
                    bw.close();
                    bw = null;
                    System.out.println();
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                } finally {
                    if (bw != null) {
                        bw.close();
                    }
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e1) {
                }
            }
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    /**
     * Parse message, translating the text not inside html
     *
     * @param is
     * @param e
     * @throws IOException
     */
    void parse(InputStream is, EventProcessor e) throws IOException {
        StringBuffer sb = new StringBuffer();
        int ch = 0;
        while (ch != -1) {
            if (isMarkup(ch) == false) {
                ch = is.read();
            }
            if (ch == -1) {
                break;
            }
            // System.out.println("CH=" + (char) ch + " = " + ch);
            if ((char) ch == '<') {
                notifyContent(sb, e);
                ch = skipMarkup('<', '>', is, e);
                continue;
            }
            if ((char) ch == '{') {
                notifyContent(sb, e);
                ch = skipMarkup('{', '}', is, e);
                continue;
            }
            if (isContent(ch)) {
                sb.append((char) ch);
            } else {
                notifyContent(sb, e);
                ch = skipToContent(ch, is, e);
                if (ch != -1 && isMarkup(ch) == false) {
                    sb.append((char) ch);
                }
            }
        }
        notifyContent(sb, e);
    }

    void notifyContent(StringBuffer sb, EventProcessor e) throws IOException {
        if (sb.length() > 0) {
            e.foundContent(sb.toString());
            sb.setLength(0);
        }
    }

    int skipToContent(int ch, InputStream is, EventProcessor e)
            throws IOException {
        StringBuffer sb = new StringBuffer();
        sb.append((char) ch);
        while (true) {
            ch = is.read();
            if (ch == -1) {
                break;
            }
            if (isContent(ch) || isMarkup(ch)) {
                break;
            } else {
                sb.append((char) ch);
            }
        }
        e.foundMarkup(sb.toString());
        return ch;
    }

    int skipMarkup(int start, int end, InputStream is, EventProcessor e)
            throws IOException {
        StringBuffer sb = new StringBuffer();
        sb.append((char) start);
        int ch = 0;
        while (true) {
            ch = is.read();
            if (ch == -1) {
                break;
            }
            sb.append((char) ch);
            if (ch == end) {
                break;
            }
        }
        e.foundMarkup(sb.toString());
        return ch;
    }

    boolean isContent(int c) {
        if ((c >= 'a') && (c <= 'z'))
            return true; // lowercase
        if ((c >= 'A') && (c <= 'Z'))
            return true; // uppercase
        if ((c >= '0') && (c <= '9'))
            return true; // numeric
        if (c == ' ' || c == '\t')
            return true;
        return false;
    }

    boolean isMarkup(int c) {
        if (c == '{' || c == '<')
            return true;
        return false;
    }
}

class EventProcessor {
    Boolean debug;
    String sourceLanguage;
    String destLanguage;
    Writer writer;

    public EventProcessor(Boolean debug, String sourceLanguage,
            String destLanguage, Writer writer) {
    	//Set referrer 
    	Translate.setHttpReferrer("http://localhost");
        this.debug = debug;
        this.sourceLanguage = sourceLanguage;
        this.destLanguage = destLanguage;
        this.writer = writer;
    }

    public void foundMarkup(String markup) throws IOException {
        // if (debug) {
        // System.out.println("markup0:[" + markup + "]");
        // }
        markup = markup.replaceAll("\n", "\\\\n");
        writer.write(markup);
        if (markup.equalsIgnoreCase("<br>")) {
            writer.write("\\\r\n");
        }
    }

    public void foundContent(String content) throws IOException {
//        if (debug) {
//            System.out.println("\n[" + content + "]BEFORE");
//        }
        String translatedText = content;
        try {
            if (content.trim().length() > 0) {
                // extract whitespace from start
                String prefix = stripFront(content);
                // extract whitespace from end
                String suffix = stripEnd(content);
                content = content.trim();
                translatedText = Translate.execute(content, Language.fromString(sourceLanguage), Language.fromString(destLanguage));
                translatedText = (new StringBuilder()).append(prefix).append(translatedText).append(suffix).toString();
            }
        } catch (Exception e) {
            System.out.println("ERROR src:[" + content + "] srcLang:"
                    + sourceLanguage + " dstLang:" + destLanguage + " err:"
                    + e.getMessage());
        }
        if(debug.booleanValue()) {
        	System.out.println((new StringBuilder()).append("[").append(translatedText).append("]").toString());
        }
        writer.write(translatedText);
    }

    // Return the whitespace from the front of a string
    private String stripFront(String s) {
        int len = s.length();
        int st = 0;
        char[] val = s.toCharArray();
        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        return (st > 0) ? s.substring(0, st) : "";
    }

    // Return the whitespace from the end of a string
    private String stripEnd(String s) {
        int len = s.length();
        int c = 0;
        char[] val = s.toCharArray();
        while (len > 0 && val[len - 1] <= ' ') {
            len--;
            c++;
        }
        return (c > 0) ? s.substring(s.length() - c) : "";
    }
}